package cn.apienvrypt.interceptor;

import java.io.InputStreamReader;
import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import cn.apienvrypt.annotation.EncParam;
import cn.apienvrypt.config.SecretKeyConfig;
import cn.apienvrypt.context.HandleContext;
import cn.apienvrypt.domain.CommonDto;
import cn.apienvrypt.domain.RequestDto;
import cn.apienvrypt.domain.SignDto;
import cn.apienvrypt.util.*;

/**
 * App参数使用自定义注解VParam
 *
 * @author lfj
 */
@Configuration
public class EncParamHandler implements HandlerMethodArgumentResolver {

	@Resource
	private SecretKeyConfig secretKeyConfig;
	private final Logger log = LoggerFactory.getLogger(this.getClass());

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(EncParam.class);
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		if(request != null && request.getAttribute(RequestDto.SIGN) == null) {
			return firstHandle(parameter, request);
		}
		return handle(parameter, request);
	}

	private Object firstHandle(MethodParameter parameter, HttpServletRequest request) {
		StringBuilder logString = new StringBuilder();

		try {
			// 使用FilterGzip支持gzip
			ServletInputStream inputStream = request.getInputStream();
			JsonElement element = JsonParser.parseReader(new InputStreamReader(inputStream));
			if(element.isJsonNull() || !element.isJsonObject()) {
//				// swagger测试
//				if(request.getSession()
//					.getAttribute("vUser") != null) {
//					return handleTest(parameter, request);
//				}
				return null;
			}
			JsonObject body = element.getAsJsonObject();
			// 处理后放入Attribute中
			request.setAttribute(RequestDto.BODY, body);

			logString.append("\n")
				.append(body.toString());

			String signSource = body.get(RequestDto.SIGN)
				.getAsString();
			String signString;
			try {
				signString = new String(RSAUtil.decrypt(Base64Util.decode(signSource), secretKeyConfig.getPrivateKey()), secretKeyConfig.getCharset());
			} catch(Exception e) {
				throw new RuntimeException("sign解密失败");
			}

			logString.append("\nsign: ")
				.append(signString);

			Gson gson = GsonUtil.getGson();
			SignDto signDto = gson.fromJson(signString, SignDto.class);

			CommonDto commonDto = gson.fromJson(body.get(RequestDto.COMMON)
				.getAsString(), CommonDto.class);
			request.setAttribute(RequestDto.COMMON, commonDto);

			request.setAttribute(RequestDto.SIGN, signDto);
			String lanSource = body.get(RequestDto.LAN)
				.getAsString();
			request.setAttribute(RequestDto.LAN, lanSource);

			// 不加密字段
			JsonObject ncJson = ParamHandleUtil.toJsonObject(body.get(RequestDto.NC));
			request.setAttribute(RequestDto.NC, ncJson);

			// 加密字段
			JsonObject ecJson = null;
			if(body.has(RequestDto.EC)) {
				String ecSource = body.get(RequestDto.EC)
					.getAsString();
				if(ecSource != null && !"".equals(ecSource)) {
					String ecString = AESUtil.getInstance()
						.decrypt(signDto.getKey(), ecSource);
					if(ecString != null && !"".equals(ecString)) {
						HandleContext.setValue(signDto.getKey());
						ecJson = JsonParser.parseString(ecString)
							.getAsJsonObject();

					} else {
						throw new RuntimeException("解密失败");
					}
					request.setAttribute(RequestDto.EC, ecJson);
				}
			}

			log.warn(logString.toString());
			return handle(parameter, body, lanSource, commonDto, ncJson, signDto, ecJson);
		} catch(Exception e) {
			throw new RuntimeException("参数错误,结构不正确。" + e.getMessage());
		}
	}

	/**
	 * 尽量减少一点时间
	 */
	private Object handle(MethodParameter parameter, HttpServletRequest request) throws Exception {
		String parameterName = parameter.getParameterName();
		SignDto signDto = (SignDto) request.getAttribute(RequestDto.SIGN);
		if(RequestDto.SIGN.equals(parameterName)) {
			return signDto;
		}
		CommonDto commonDto = (CommonDto) request.getAttribute(RequestDto.COMMON);
		if(RequestDto.COMMON.equals(parameterName)) {
			return commonDto;
		}
		String lanSource = (String) request.getAttribute(RequestDto.LAN);
		if(RequestDto.LAN.equals(parameterName)) {
			return lanSource;
		}
		JsonObject ecJson = (JsonObject) request.getAttribute(RequestDto.EC);
		if(RequestDto.EC.equals(parameterName)) {
			return ecJson;
		}
		JsonObject ncJson = (JsonObject) request.getAttribute(RequestDto.NC);
		if(RequestDto.NC.equals(parameterName)) {
			return ncJson;
		}
		JsonObject body = (JsonObject) request.getAttribute(RequestDto.BODY);
		return handle(parameter, body, lanSource, commonDto, ncJson, signDto, ecJson);
	}

	private Object handle(MethodParameter parameter, JsonObject body, String lanSource, CommonDto commonDto,
		JsonObject ncJson, SignDto signDto, JsonObject ecJson) {

		EncParam encParam = parameter.getParameterAnnotation(EncParam.class);
		boolean required = encParam.required();
		Class<?> parameterType = parameter.getParameterType();
		Class<?> typeInList = encParam.typeInList();
		String parameterName = parameter.getParameterName();

		try {
			if(RequestDto.SIGN.equals(parameterName)) {
				return signDto;
			}
			if(RequestDto.COMMON.equals(parameterName)) {
				return commonDto;
			}
			if(RequestDto.LAN.equals(parameterName)) {
				return lanSource;
			}
			if(RequestDto.EC.equals(parameterName)) {
				return ecJson;
			}
			if(RequestDto.NC.equals(parameterName)) {
				return ncJson;
			}
			// 默认先取nc不加密的
			if(ncJson.has(parameterName)) {
				return ParamHandleUtil.result(ncJson.get(parameterName), required, parameterName, parameterType, typeInList);
			}
			if(ecJson != null && ecJson.has(parameterName)) {
				return ParamHandleUtil.result(ecJson.get(parameterName), required, parameterName, parameterType, typeInList);
			}
		} catch(Exception e) {
			log.error("参数错误:", e);
			throw new RuntimeException("参数错误，结构不正确。" + e.getMessage());
		}
		if(required) {
			throw new RuntimeException("参数错误，缺少" + parameterName + "，其类型为" + parameterType.getSimpleName());
		} else if(!body.has(parameterName)) {
			return null;
		}

		return GsonUtil.getGson()
			.fromJson(body.get(parameterName)
				.toString(), parameterType);
	}
}
